﻿#include <QTimer>
#include <QSize>
#include <QDebug>
#include <QVariant>
#include <QRect>
#include <QIcon>
#include <QCursor>

#include <algorithm>

#include "CefWebEngineCore/CefWebEnginePage.h"

#include "CefWebEnginePrivate.h"


static void stringMapToCommandLine(const QMap<QString, QString> &map, CefRefPtr<CefCommandLine> commandLine)
{
    for (auto it = map.begin(); it != map.end(); ++it)
    {
        QString key = it.key();
        QString value = it.value();
        if (!key.isEmpty())
        {
            if (!value.isEmpty())
                commandLine->AppendSwitchWithValue(key.toStdString(), value.toStdString());
            else
                commandLine->AppendSwitch(key.toStdString());
        }
    }
}

static Qt::CursorShape toQCursorShape(cef_cursor_type_t type)
{
    static Qt::CursorShape d [] = {
      Qt::ArrowCursor,        // CT_POINTER = 0,
      Qt::CrossCursor,        // CT_CROSS,
      Qt::PointingHandCursor, // CT_HAND,
      Qt::IBeamCursor,        // CT_IBEAM,
      Qt::WaitCursor,         // CT_WAIT,
      Qt::WhatsThisCursor,    // CT_HELP,
      Qt::SizeHorCursor,      // CT_EASTRESIZE,
      Qt::SizeVerCursor,      // CT_NORTHRESIZE,
      Qt::SizeBDiagCursor,    // CT_NORTHEASTRESIZE,
      Qt::SizeFDiagCursor,    // CT_NORTHWESTRESIZE,
      Qt::SizeVerCursor,      // CT_SOUTHRESIZE,
      Qt::SizeFDiagCursor,    // CT_SOUTHEASTRESIZE,
      Qt::SizeBDiagCursor,    // CT_SOUTHWESTRESIZE,
      Qt::SizeHorCursor,      // CT_WESTRESIZE,
      Qt::SizeVerCursor,      // CT_NORTHSOUTHRESIZE,
      Qt::SizeHorCursor,      // CT_EASTWESTRESIZE,
      Qt::SizeAllCursor,      // CT_NORTHEASTSOUTHWESTRESIZE,
      Qt::SizeAllCursor,      // CT_NORTHWESTSOUTHEASTRESIZE,
      Qt::SplitHCursor,       // CT_COLUMNRESIZE,
      Qt::SplitVCursor,       // CT_ROWRESIZE,
      Qt::ArrowCursor,        // CT_MIDDLEPANNING,
      Qt::ArrowCursor,        // CT_EASTPANNING,
      Qt::ArrowCursor,        // CT_NORTHPANNING,
      Qt::ArrowCursor,        // CT_NORTHEASTPANNING,
      Qt::ArrowCursor,        // CT_NORTHWESTPANNING,
      Qt::ArrowCursor,        // CT_SOUTHPANNING,
      Qt::ArrowCursor,        // CT_SOUTHEASTPANNING,
      Qt::ArrowCursor,        // CT_SOUTHWESTPANNING,
      Qt::ArrowCursor,        // CT_WESTPANNING,
      Qt::ArrowCursor,        // CT_MOVE,
      Qt::ArrowCursor,        // CT_VERTICALTEXT,
      Qt::ArrowCursor,        // CT_CELL,
      Qt::ArrowCursor,        // CT_CONTEXTMENU,
      Qt::ArrowCursor,        // CT_ALIAS,
      Qt::ArrowCursor,        // CT_PROGRESS,
      Qt::ArrowCursor,        // CT_NODROP,
      Qt::ArrowCursor,        // CT_COPY,
      Qt::ArrowCursor,        // CT_NONE,
      Qt::ForbiddenCursor,    // CT_NOTALLOWED,
      Qt::ArrowCursor,        // CT_ZOOMIN,
      Qt::ArrowCursor,        // CT_ZOOMOUT,
      Qt::OpenHandCursor,     // CT_GRAB,
      Qt::ClosedHandCursor,   // CT_GRABBING,
      Qt::ArrowCursor,        // CT_MIDDLE_PANNING_VERTICAL,
      Qt::ArrowCursor,        // CT_MIDDLE_PANNING_HORIZONTAL,
      Qt::ArrowCursor,        // CT_CUSTOM,
      Qt::ArrowCursor,        // CT_DND_NONE,
      Qt::DragMoveCursor,     // CT_DND_MOVE,
      Qt::DragCopyCursor,     // CT_DND_COPY,
      Qt::DragLinkCursor,     // CT_DND_LINK,
    };
    return d[type];
}



class SchemeHandler : public CefResourceHandler
{
public:
    SchemeHandler();

    bool Open(CefRefPtr<CefRequest> request, bool &handle_request, CefRefPtr<CefCallback> callback) override;

    void GetResponseHeaders(CefRefPtr<CefResponse> response,
        int64_t &response_length,
        CefString &redirectUrl) override;

    bool Skip(int64_t bytes_to_skip, int64_t &bytes_skipped, CefRefPtr<CefResourceSkipCallback> callback) override;

    bool Read(void *data_out,
        int bytes_to_read,
        int &bytes_read,
        CefRefPtr<CefResourceReadCallback> callback) override;

    void Cancel() override;

private:
    std::string mdata;
    std::string mmimeType;
    int moffset;

private:
    IMPLEMENT_REFCOUNTING(SchemeHandler);
};

SchemeHandler::SchemeHandler()
    : moffset(0)
{
}

bool SchemeHandler::Open(CefRefPtr<CefRequest> request, bool &handle_request, CefRefPtr<CefCallback> callback)
{
    handle_request = true;

    mdata = "ok";
    mmimeType = "text/html";

    return true;
}

void SchemeHandler::GetResponseHeaders(CefRefPtr<CefResponse> response, int64_t &response_length, CefString &redirectUrl)
{
    CEF_REQUIRE_IO_THREAD();
    DCHECK(!mdata.empty());
    response->SetMimeType(mmimeType);
    response->SetStatus(200);
    response_length = mdata.length();
}

bool SchemeHandler::Skip(int64_t bytes_to_skip, int64_t &bytes_skipped, CefRefPtr<CefResourceSkipCallback> callback)
{
    bytes_skipped = 0;

    moffset += bytes_to_skip;
    if (moffset <= (int)mdata.length()) {
        bytes_skipped = bytes_to_skip;
    }
    else {
        moffset = (int)mdata.length();
        bytes_skipped = mdata.length() - moffset;
    }

    return true;
}

bool SchemeHandler::Read(void *data_out, int bytes_to_read, int &bytes_read, CefRefPtr<CefResourceReadCallback> callback)
{
    bytes_read = 0;
    if (moffset < (int)mdata.length())
    {
        int transfer_size = std::min(bytes_to_read, static_cast<int>(mdata.length() - moffset));
        memcpy(data_out, mdata.c_str() + moffset, transfer_size);
        moffset += transfer_size;
        bytes_read = transfer_size;
    }
    return bytes_read > 0;
}

void SchemeHandler::Cancel()
{
}



class SchemeHandlerFactory : public CefSchemeHandlerFactory
{
public:
    virtual CefRefPtr<CefResourceHandler> Create(CefRefPtr<CefBrowser> browser,
        CefRefPtr<CefFrame> frame,
        const CefString &scheme_name,
        CefRefPtr<CefRequest> request) override;

private:
    IMPLEMENT_REFCOUNTING(SchemeHandlerFactory);
};

CefRefPtr<CefResourceHandler> SchemeHandlerFactory::Create(CefRefPtr<CefBrowser> browser,
    CefRefPtr<CefFrame> frame,
    const CefString &scheme_name,
    CefRefPtr<CefRequest> request)
{
    return new SchemeHandler();
}



class CefViewQueryHandler : public CefBaseRefCounted, public CefMessageRouterBrowserSide::Handler
{
    IMPLEMENT_REFCOUNTING(CefViewQueryHandler);

public:
    CefViewQueryHandler();
    ~CefViewQueryHandler();

    bool OnQuery(CefRefPtr<CefBrowser> browser,
        CefRefPtr<CefFrame> frame,
        int64_t query_id,
        const CefString &request,
        bool persistent,
        CefRefPtr<Callback> callback) override;

    void OnQueryCanceled(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, int64_t query_id) override;

    bool Response(int64_t query, bool success, const CefString &response, int error);

private:
    QMap<int64_t, CefRefPtr<Callback>> mcallbacks;
    QMutex mmutex;
};

CefViewQueryHandler::CefViewQueryHandler()
{
}

CefViewQueryHandler::~CefViewQueryHandler()
{
}

bool CefViewQueryHandler::OnQuery(CefRefPtr<CefBrowser> browser,
    CefRefPtr<CefFrame> frame,
    int64_t query_id,
    const CefString &request,
    bool persistent,
    CefRefPtr<Callback> callback)
{
    mmutex.lock();
    mcallbacks[query_id] = callback;
    mmutex.unlock();

    // int browserId = browser->GetIdentifier();
    // QString req = QString::fromStdU16String(request.ToString16());
    //emit pCefViewPrivate_->q_ptr->cefQueryRequest(browserId, frameId, QCefQuery(req, query_id));

    return true;
}

void CefViewQueryHandler::OnQueryCanceled(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, int64_t query_id)
{
    mmutex.lock();
    auto it = mcallbacks.find(query_id);
    if (it != mcallbacks.end())
        mcallbacks.erase(it);

    mmutex.unlock();
}

bool CefViewQueryHandler::Response(int64_t query, bool success, const CefString &response, int error)
{
    CefRefPtr<Callback> cb;
    mmutex.lock();
    auto it = mcallbacks.find(query);
    if (it != mcallbacks.end())
    {
        cb = it.value();
        mcallbacks.erase(it);
    }
    mmutex.unlock();

    if (!cb)
        return false;

    if (success)
        cb->Success(response);
    else
        cb->Failure(error, response);

    return true;
}



InternalCefClient::InternalCefClient(CefWebEnginePrivate *webEngine)
    : mresourceManager(new CefResourceManager),
    mwebEngine(webEngine)
{
    mmessageRouterConfig.js_query_function = "CefViewQuery";
    mmessageRouterConfig.js_cancel_function = "CefViewQueryCancel";

    mmessageRouter = CefMessageRouterBrowserSide::Create(mmessageRouterConfig);
    mcefqueryHandler = new CefViewQueryHandler;
    mmessageRouter->AddHandler(mcefqueryHandler.get(), false);
}

InternalCefClient::~InternalCefClient()
{
    mmessageRouter->RemoveHandler(mcefqueryHandler.get());
}

CefRefPtr<CefLifeSpanHandler> InternalCefClient::GetLifeSpanHandler()
{
    return this;
}

bool InternalCefClient::OnBeforePopup(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame,
#if CEF_VERSION_MAJOR >= 139
    int popup_id,
#endif
    const CefString &target_url, const CefString &target_frame_name,
    CefLifeSpanHandler::WindowOpenDisposition target_disposition, bool user_gesture,
    const CefPopupFeatures &popupFeatures, CefWindowInfo &windowInfo, CefRefPtr<CefClient> &client,
    CefBrowserSettings &settings, CefRefPtr<CefDictionaryValue> &extra_info, bool *no_javascript_access)
{
    CEF_REQUIRE_UI_THREAD();

    bool cancel = true;

    if (target_disposition ==
#if CEF_VERSION_MAJOR >= 139
        CefLifeSpanHandler::WindowOpenDisposition::CEF_WOD_NEW_POPUP
#else
        CefLifeSpanHandler::WindowOpenDisposition::WOD_NEW_POPUP
#endif
        )
    {
    }
    else
    {
    }

    if (CefWebEnginePage *page = findPage(browser))
    {
        CefWebEnginePage *newPage = nullptr;
        emit page->newWindowRequested(QString::fromStdString(target_url), &newPage);
        if (newPage)
        {
            quintptr parent = newPage->property("windowId").value<quintptr>();
            windowInfo.SetAsChild((CefWindowHandle)parent, CefRect(0, 0, INT_MAX, INT_MAX));
            cancel = false;
        }
    }

    return cancel;
}

void InternalCefClient::OnAfterCreated(CefRefPtr<CefBrowser> browser)
{
    CefWebEnginePage *page = findPage(browser);
    if (page && !page->hasBrowser())
        page->setBrowser(browser.get());
}

bool InternalCefClient::DoClose(CefRefPtr<CefBrowser> browser)
{
    return false;
}

void InternalCefClient::OnBeforeClose(CefRefPtr<CefBrowser> browser)
{
    mmessageRouter->OnBeforeClose(browser);
}

CefRefPtr<CefContextMenuHandler> InternalCefClient::GetContextMenuHandler()
{
    return this;
}

void InternalCefClient::OnBeforeContextMenu(CefRefPtr<CefBrowser> browser,
    CefRefPtr<CefFrame> frame, CefRefPtr<CefContextMenuParams> params, CefRefPtr<CefMenuModel> model)
{
}

bool InternalCefClient::RunContextMenu(CefRefPtr<CefBrowser> browser,
    CefRefPtr<CefFrame> frame, CefRefPtr<CefContextMenuParams> params, CefRefPtr<CefMenuModel> model, CefRefPtr<CefRunContextMenuCallback> callback)
{
    return false;
}

bool InternalCefClient::OnContextMenuCommand(CefRefPtr<CefBrowser> browser,
    CefRefPtr<CefFrame> frame, CefRefPtr<CefContextMenuParams> params, int command_id, EventFlags event_flags)
{
    return false;
}

void InternalCefClient::OnContextMenuDismissed(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame)
{
}

CefRefPtr<CefDialogHandler> InternalCefClient::GetDialogHandler()
{
    return CefRefPtr<CefDialogHandler>();
}

bool InternalCefClient::OnFileDialog(CefRefPtr<CefBrowser> browser,
    FileDialogMode mode, const CefString &title, const CefString &default_file_path,
    const std::vector<CefString> &accept_filters,
#if CEF_VERSION_MAJOR >= 139
    const std::vector<CefString> &accept_extensions,
    const std::vector<CefString> &accept_descriptions,
#endif
    CefRefPtr<CefFileDialogCallback> callback)
{
    return false;
}

CefRefPtr<CefDisplayHandler> InternalCefClient::GetDisplayHandler()
{
    return this;
}

void InternalCefClient::OnAddressChange(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, const CefString &url)
{
    if (CefWebEnginePage *page = findPage(browser))
        emit page->urlChanged(QString::fromStdString(url));
}

void InternalCefClient::OnTitleChange(CefRefPtr<CefBrowser> browser, const CefString &title)
{
    if (CefWebEnginePage *page = findPage(browser))
        page->setTitle(QString::fromStdString(title));
}

void InternalCefClient::OnFaviconURLChange(CefRefPtr<CefBrowser> browser, const std::vector<CefString> &icon_urls)
{
    QList<QUrl> urls;
    for (const CefString &iconUrl : icon_urls)
        urls.append(QString::fromStdString(iconUrl));

    if (urls.isEmpty())
        return;

    if (CefWebEnginePage *page = findPage(browser))
        emit page->faviconUrlChanged(urls);
}

void InternalCefClient::OnFullscreenModeChange(CefRefPtr<CefBrowser> browser, bool fullscreen)
{
    if (CefWebEnginePage *page = findPage(browser))
        emit page->fullScreenRequested(fullscreen);
}

bool InternalCefClient::OnTooltip(CefRefPtr<CefBrowser> browser, CefString &text)
{
    if (CefWebEnginePage *page = findPage(browser))
        emit page->toolTipChanged(QString::fromStdString(text));

    return false;
}

void InternalCefClient::OnStatusMessage(CefRefPtr<CefBrowser> browser, const CefString &value)
{
    if (CefWebEnginePage *page = findPage(browser))
        emit page->statusChanged(QString::fromStdString(value));
}

bool InternalCefClient::OnConsoleMessage(CefRefPtr<CefBrowser> browser,
    cef_log_severity_t level, const CefString &message, const CefString &source, int line)
{
    qInfo().noquote() << "CEF:" << QString::fromStdString(message)
        << " at " << QString::fromStdString(source) << ":" << line;

    return true;
}

bool InternalCefClient::OnAutoResize(CefRefPtr<CefBrowser> browser, const CefSize &new_size)
{
    bool cancel = true;
    if (CefWebEnginePage *page = findPage(browser))
    {
        bool accept = false;
        emit page->resizeRequested(QSize(new_size.width, new_size.height), accept);
        cancel = !accept;
    }
    return cancel;
}

bool InternalCefClient::OnCursorChange(CefRefPtr<CefBrowser> browser,
    CefCursorHandle cursor, cef_cursor_type_t type, const CefCursorInfo &custom_cursor_info)
{
    if (type == CT_CUSTOM)
        return false;

    if (CefWebEnginePage *page = findPage(browser))
        emit page->cursorChanged(toQCursorShape(type));

    return false;
}

CefRefPtr<CefKeyboardHandler> InternalCefClient::GetKeyboardHandler()
{
    return this;
}

bool InternalCefClient::OnPreKeyEvent(CefRefPtr<CefBrowser> browser,
    const CefKeyEvent &event, CefEventHandle os_event, bool *is_keyboard_shortcut)
{
    return false;
}

bool InternalCefClient::OnKeyEvent(CefRefPtr<CefBrowser> browser,
    const CefKeyEvent &event, CefEventHandle os_event)
{
    return false;
}

CefRefPtr<CefLoadHandler> InternalCefClient::GetLoadHandler()
{
    return this;
}

void InternalCefClient::OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
    bool isLoading, bool canGoBack, bool canGoForward)
{
}

void InternalCefClient::OnLoadStart(CefRefPtr<CefBrowser> browser,
    CefRefPtr<CefFrame> frame, TransitionType transition_type)
{
    if (CefWebEnginePage *page = findPage(browser))
        emit page->loadStarted();
}

void InternalCefClient::OnLoadEnd(CefRefPtr<CefBrowser> browser,
    CefRefPtr<CefFrame> frame, int httpStatusCode)
{
    if (CefWebEnginePage *page = findPage(browser))
        emit page->loadFinished(httpStatusCode == 1692005495);
}

void InternalCefClient::OnLoadError(CefRefPtr<CefBrowser> browser,
    CefRefPtr<CefFrame> frame, ErrorCode errorCode, const CefString &errorText, const CefString &failedUrl)
{
    if (CefWebEnginePage *page = findPage(browser))
        emit page->errorOccurred(QString::fromStdString(errorText));
}

void InternalCefClient::OnLoadingProgressChange(CefRefPtr<CefBrowser> browser, double progress)
{
    if (CefWebEnginePage *page = findPage(browser))
        emit page->loadProgressChanged(static_cast<int>(progress * 100));
}

CefRefPtr<CefRenderHandler> InternalCefClient::GetRenderHandler()
{
    return this;
}

CefRefPtr<CefAccessibilityHandler> InternalCefClient::GetAccessibilityHandler()
{
    return nullptr;
}

bool InternalCefClient::GetRootScreenRect(CefRefPtr<CefBrowser> browser, CefRect &rect)
{
    return false;
}

void InternalCefClient::GetViewRect(CefRefPtr<CefBrowser> browser, CefRect &rect)
{
}

bool InternalCefClient::GetScreenPoint(CefRefPtr<CefBrowser> browser, int viewX, int viewY, int &screenX, int &screenY)
{
    return false;
}

bool InternalCefClient::GetScreenInfo(CefRefPtr<CefBrowser> browser, CefScreenInfo &screen_info)
{
    return false;
}

void InternalCefClient::OnPopupShow(CefRefPtr<CefBrowser> browser, bool show)
{
}

void InternalCefClient::OnPopupSize(CefRefPtr<CefBrowser> browser, const CefRect &rect)
{
}

void InternalCefClient::OnPaint(CefRefPtr<CefBrowser> browser,
    PaintElementType type, const RectList &dirtyRects, const void *buffer, int width, int height)
{
}

void InternalCefClient::OnAcceleratedPaint(CefRefPtr<CefBrowser> browser,
    PaintElementType type, const RectList &dirtyRects,
#if CEF_VERSION_MAJOR >= 139
    const CefAcceleratedPaintInfo &info
#else
    void *shared_handle
#endif
    )
{
}

bool InternalCefClient::StartDragging(CefRefPtr<CefBrowser> browser,
    CefRefPtr<CefDragData> drag_data, CefRenderHandler::DragOperationsMask allowed_ops, int x, int y)
{
    return false;
}

void InternalCefClient::UpdateDragCursor(CefRefPtr<CefBrowser> browser, DragOperation operation)
{
}

void InternalCefClient::OnScrollOffsetChanged(CefRefPtr<CefBrowser> browser, double x, double y)
{
}

void InternalCefClient::OnImeCompositionRangeChanged(CefRefPtr<CefBrowser> browser, const CefRange &selected_range, const RectList &character_bounds)
{
}

void InternalCefClient::OnTextSelectionChanged(CefRefPtr<CefBrowser> browser, const CefString &selected_text, const CefRange &selected_range)
{
}

void InternalCefClient::OnVirtualKeyboardRequested(CefRefPtr<CefBrowser> browser, TextInputMode input_mode)
{
}

CefRefPtr<CefDragHandler> InternalCefClient::GetDragHandler()
{
    return this;
}

bool InternalCefClient::OnDragEnter(CefRefPtr<CefBrowser> browser, CefRefPtr<CefDragData> dragData, CefDragHandler::DragOperationsMask mask)
{
    return false;
}

void InternalCefClient::OnDraggableRegionsChanged(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, const std::vector<CefDraggableRegion> &regions)
{
}

CefRefPtr<CefFocusHandler> InternalCefClient::GetFocusHandler()
{
    return this;
}

void InternalCefClient::OnTakeFocus(CefRefPtr<CefBrowser> browser, bool next)
{
}

bool InternalCefClient::OnSetFocus(CefRefPtr<CefBrowser> browser, FocusSource source)
{
    return false;
}

void InternalCefClient::OnGotFocus(CefRefPtr<CefBrowser> browser)
{
}

CefRefPtr<CefJSDialogHandler> InternalCefClient::GetJSDialogHandler()
{
    return this;
}

bool InternalCefClient::OnJSDialog(CefRefPtr<CefBrowser> browser,
    const CefString &origin_url, JSDialogType dialog_type, const CefString &message_text,
    const CefString &default_prompt_text, CefRefPtr<CefJSDialogCallback> callback, bool &suppress_message)
{
    return false;
}

bool InternalCefClient::OnBeforeUnloadDialog(CefRefPtr<CefBrowser> browser,
    const CefString &message_text, bool is_reload, CefRefPtr<CefJSDialogCallback> callback)
{
    return false;
}

void InternalCefClient::OnResetDialogState(CefRefPtr<CefBrowser> browser)
{
}

CefRefPtr<CefRequestHandler> InternalCefClient::GetRequestHandler()
{
    return this;
}

bool InternalCefClient::OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
    CefRefPtr<CefFrame> frame, CefRefPtr<CefRequest> request, bool user_gesture, bool is_redirect)
{
    CEF_REQUIRE_UI_THREAD();

    mmessageRouter->OnBeforeBrowse(browser, frame);
    return false;
}

bool InternalCefClient::OnOpenURLFromTab(CefRefPtr<CefBrowser> browser,
    CefRefPtr<CefFrame> frame, const CefString &target_url, CefRequestHandler::WindowOpenDisposition target_disposition, bool user_gesture)
{
    CEF_REQUIRE_UI_THREAD();
    return false;
}

void InternalCefClient::OnRenderProcessTerminated(CefRefPtr<CefBrowser> browser, TerminationStatus status
#if CEF_VERSION_MAJOR >= 139
    , int error_code,
    const CefString &error_string
#endif
    )
{
    CEF_REQUIRE_UI_THREAD();
    mmessageRouter->OnRenderProcessTerminated(browser);
    if (browser)
    {
        CefString url = browser->GetMainFrame()->GetURL();
        if (!url.empty())
            browser->GetMainFrame()->LoadURL(url);
    }
}

CefRefPtr<CefResourceRequestHandler> InternalCefClient::GetResourceRequestHandler(CefRefPtr<CefBrowser> browser,
    CefRefPtr<CefFrame> frame, CefRefPtr<CefRequest> request, bool is_navigation, bool is_download,
    const CefString &request_initiator, bool &disable_default_handling)
{
    return this;
}

CefResourceRequestHandler::ReturnValue InternalCefClient::OnBeforeResourceLoad(CefRefPtr<CefBrowser> browser,
    CefRefPtr<CefFrame> frame, CefRefPtr<CefRequest> request, CefRefPtr<CefCallback> callback)
{
    auto url = request->GetURL().ToString();
    return mresourceManager->OnBeforeResourceLoad(browser, frame, request, callback);
}

CefRefPtr<CefResourceHandler> InternalCefClient::GetResourceHandler(CefRefPtr<CefBrowser> browser,
    CefRefPtr<CefFrame> frame, CefRefPtr<CefRequest> request)
{
    return mresourceManager->GetResourceHandler(browser, frame, request);
}

void InternalCefClient::OnProtocolExecution(CefRefPtr<CefBrowser> browser,
    CefRefPtr<CefFrame> frame, CefRefPtr<CefRequest> request, bool &allow_os_execution)
{
}

CefRefPtr<CefDownloadHandler> InternalCefClient::GetDownloadHandler()
{
    return this;
}

#if CEF_VERSION_MAJOR >= 139
bool
#else
void
#endif
InternalCefClient::OnBeforeDownload(CefRefPtr<CefBrowser> browser,
    CefRefPtr<CefDownloadItem> download_item, const CefString &suggested_name, CefRefPtr<CefBeforeDownloadCallback> callback)
{
#if CEF_VERSION_MAJOR >= 139
    return false;
#endif
}

void InternalCefClient::OnDownloadUpdated(CefRefPtr<CefBrowser> browser,
    CefRefPtr<CefDownloadItem> download_item, CefRefPtr<CefDownloadItemCallback> callback)
{
}

CefWebEnginePage *InternalCefClient::findPage(CefRefPtr<CefBrowser> browser) const
{
    quintptr targetWindow = (quintptr)GetParent(browser->GetHost()->GetWindowHandle());
    for (CefWebEnginePage *page : mwebEngine->mpages)
    {
        quintptr id = page->property("windowId").value<quintptr>();
        if (id == targetWindow)
            return page;
    }
    return nullptr;
}



InternalCefApp::InternalCefApp(CefWebEnginePrivate *webEngine, const QString &bridgeName)
    : mwebEngine(webEngine),
    mbridgeName(bridgeName)
{
}

InternalCefApp::~InternalCefApp()
{
}

void InternalCefApp::RegisterCustomSchemesHandlerFactories()
{
    CefRegisterSchemeHandlerFactory("CefView", "", new SchemeHandlerFactory());
}

void InternalCefApp::RegisterCustomSchemes(CefRawPtr<CefSchemeRegistrar> registrar)
{
    registrar->AddCustomScheme("CefView", 0);
}

void InternalCefApp::OnBeforeCommandLineProcessing(const CefString &process_type, CefRefPtr<CefCommandLine> command_line)
{
    stringMapToCommandLine(mwebEngine->mswitchs, command_line);
}

void InternalCefApp::OnRegisterCustomSchemes(CefRawPtr<CefSchemeRegistrar> registrar)
{
}

CefRefPtr<CefBrowserProcessHandler> InternalCefApp::GetBrowserProcessHandler()
{
    return this;
}

void InternalCefApp::OnContextInitialized()
{
    CEF_REQUIRE_UI_THREAD();
}

void InternalCefApp::OnBeforeChildProcessLaunch(CefRefPtr<CefCommandLine> command_line)
{
    stringMapToCommandLine(mwebEngine->mswitchs, command_line);
    command_line->AppendSwitchWithValue("bridge-obj-name", mbridgeName.toStdString());
}

void InternalCefApp::OnScheduleMessagePumpWork(int64_t delay_ms)
{
    int64_t delay = qMax((int64_t)0, qMin(delay_ms, 1000 / 60));
    QTimer::singleShot(delay, []() { CefDoMessageLoopWork(); });
}


CefWebEnginePrivate::CefWebEnginePrivate(CefWebEngine *q)
    : q_ptr(q),
    mapp(new InternalCefApp(this)),
    mclient(new InternalCefClient(this))
{
}

void CefWebEnginePrivate::addSwitch(const QString &key)
{
    mswitchs[key] = QString();
}

void CefWebEnginePrivate::addSwitchWithValue(const QString &key, const QString &value)
{
    mswitchs[key] = value;
}
